// t2.cpp : Defines the entry point for the application.
//
// program structure vs function
//
// structure has no immediate and obvious meaning to the end user
// structure matters to the coder
//
// function matters to both the end-user and the coder
// the end user cares only about how the program functions
// though the programmer may care about the underlying structure, he  should
// think about structure only as a means of meeting functional requirements

#include "stdafx.h"
#include "t2.h"

#define MAX_LOADSTRING 100

// Global Variables:
HINSTANCE hInst;								// current instance
TCHAR szTitle[MAX_LOADSTRING];					// The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];			// the main window class name
TCHAR szT2WindowClass[MAX_LOADSTRING];			// the main window class name

// Forward declarations of functions included in this code module:
ATOM				Main_RegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE, int);
LRESULT CALLBACK	Main_WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK	About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

 	// TODO: Place code here.
	MSG msg;
	HACCEL hAccelTable;

	// Initialize global strings
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDC_MAIN, szWindowClass, MAX_LOADSTRING);
	LoadString(hInstance, IDC_T2, szT2WindowClass, MAX_LOADSTRING);
	Main_RegisterClass(hInstance);
	T2_RegisterClass(hInstance);

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow))
	{
		return FALSE;
	}

	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_T2));

	// Main message loop:
	while (GetMessage(&msg, NULL, 0, 0))
	{
		if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return (int) msg.wParam;
}

ATOM Main_RegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= Main_WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_T2));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_T2);
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

ATOM T2_RegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= T2_WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_T2));
	wcex.hCursor		= LoadCursor(NULL, IDC_CROSS);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= MAKEINTRESOURCE(IDC_T2);
	wcex.lpszClassName	= szT2WindowClass;
	wcex.hIconSm		= LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

	return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hwnd;

   hInst = hInstance; // Store instance handle in our global variable

   hwnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hwnd)
   {
      return FALSE;
   }

   HWND hWndT2 = CreateWindow(szT2WindowClass, szTitle, WS_CHILD | WS_BORDER,
      10, 10, 100, 100, hwnd, (HMENU) ID_T2, hInstance, new T2());

   if (!hWndT2)
   {
      return FALSE;
   }

   ShowWindow(hwnd, nCmdShow);
   UpdateWindow(hwnd);

   ShowWindow(hWndT2, nCmdShow);
   UpdateWindow(hWndT2);

   return TRUE;
}

LRESULT CALLBACK Main_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		HANDLE_MSG (hwnd, WM_COMMAND, Main_OnCommand);
		HANDLE_MSG (hwnd, WM_DESTROY, Main_OnDestroy);
		HANDLE_MSG (hwnd, WM_SIZE, Main_OnSize);
	}

	return DefWindowProc(hwnd, message, wParam, lParam);
}

void Main_OnSize(HWND hwnd, UINT state, int cx, int cy)
{
	HWND hWndT2 = FindWindowEx(hwnd, NULL, szT2WindowClass, NULL);
	assert(hWndT2 != NULL);
	RECT rcMain;

	GetClientRect(hwnd, &rcMain);
	InflateRect(&rcMain, -20, -20);
	SetWindowPos(hWndT2, NULL, rcMain.left, rcMain.top,
		rcMain.right - rcMain.left, rcMain.bottom - rcMain.top, 0);

}

void Main_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
	switch (id)
	{
	case IDM_ABOUT:
		DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, About);
		break;
	case IDM_EXIT:
		DestroyWindow(hwnd);
		break;
	default:
		FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, DefWindowProc);
	}
}

void Main_OnDestroy(HWND hwnd){
	PostQuitMessage(0);
}

LRESULT CALLBACK T2_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
		HANDLE_MSG (hwnd, WM_CREATE, T2_OnCreate);
		HANDLE_MSG (hwnd, WM_PAINT, T2_OnPaint);
		HANDLE_MSG (hwnd, WM_DESTROY, T2_OnDestroy);
		HANDLE_MSG (hwnd, WM_LBUTTONUP, T2_OnLButtonUp);
		HANDLE_MSG (hwnd, WM_MOUSEMOVE, T2_OnMouseMove);
	}

	return DefWindowProc(hwnd, message, wParam, lParam);
}

INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

Point::Point(const float &x, const float &y) : x(x), y(y) { }
Point::Point(const Point& point) : x(point.x), y(point.y) { }

Wall::Wall(const Point &start, const Point &end, float thickness)
	: start(start), end(end), thickness(thickness) {

	Point_2 p2start(start.x, start.y);
	Point_2 p2end(end.x, end.y);
	Vector_2& v = p2end - p2start;

	Vector_2& perp = v.perpendicular(CGAL::COUNTERCLOCKWISE);

	double length = sqrt(to_double(perp.squared_length()));
	//double length = to_double(CGAL::sqrt(v2.squared_length()));
	double scale = thickness / length;
	Vector_2& offset = perp * scale;

	Point_2& p2startr = p2start + offset;
	Point_2& p2endr = p2end + offset;

	startr.x = (float) to_double(p2startr.x());
	startr.y = (float) to_double(p2startr.y());
	endr.x = (float) to_double(p2endr.x());
	endr.y = (float) to_double(p2endr.y());
}

void DrawWall(HDC hdc, Wall& wall){
		MoveToEx(hdc, (int) wall.start.x, (int) wall.start.y, 0);
		LineTo(hdc, (int) wall.end.x, (int) wall.end.y);
		LineTo(hdc, (int) wall.endr.x, (int) wall.endr.y);
		LineTo(hdc, (int) wall.startr.x, (int) wall.startr.y);
		LineTo(hdc, (int) wall.start.x, (int) wall.start.y);
}

void Render(HDC hdc, HWND hwnd, T2 *t2){
	RECT rect;
	GetClientRect(hwnd, &rect);
	FillRect(hdc, &rect, (HBRUSH)(COLOR_WINDOW + 1));

	std::vector<Wall>& walls = t2->walls;
	for(std::vector<Wall>::iterator itr = walls.begin();
		itr != walls.end(); itr++){

		DrawWall(hdc, *itr);
	}
}

void T2_OnPaint(HWND hwnd)
{
	T2 *t2 = (T2*) GetProp(hwnd, _T("T2"));
	assert(t2 != 0);

	PAINTSTRUCT ps;
	BeginPaint(hwnd, &ps);

	HDC hdc = ps.hdc;
	Render(hdc, hwnd, t2);
	EndPaint(hwnd, &ps);
}

void T2_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
{
	T2 *t2 = (T2*) GetProp(hwnd, _T("T2"));
	assert(t2 != 0);

	MouseEventArgs args = {
		hwnd, x, y, keyFlags
	};

	t2->mhistory.push_back(args);

	if(t2->mhistory.size() == 2){
		MouseEventArgs& mstart = t2->mhistory[0];
		MouseEventArgs& mend = t2->mhistory[1];
		t2->walls.push_back(Wall(Point((float)mstart.x, (float)mstart.y),
			Point((float)mend.x, (float)mend.y), 15.0f));
		t2->mhistory.clear();
		InvalidateRect(hwnd, 0, FALSE);
	}
	else
		assert(t2->mhistory.size() == 1);
}

void T2_OnDestroy(HWND hwnd)
{
	T2 *t2 = (T2*) RemoveProp(hwnd, _T("T2"));
	assert(t2 != 0);
	delete t2;
}

BOOL T2_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
	try {
		T2 *t2 = static_cast<T2*>(lpCreateStruct->lpCreateParams);
		if(t2 == NULL)
			return FALSE;

		SetProp(hwnd, _T("T2"), t2);

		return TRUE;
	}
	catch(std::bad_alloc&){
		return FALSE;
	}
}


void T2_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
{
	T2 *t2 = (T2*) GetProp(hwnd, _T("T2"));
	assert(t2 != 0);

	if(t2->mhistory.size() == 1){
		Wall newWall(Point((float)t2->mhistory[0].x, (float)t2->mhistory[0].y),
			Point((float)x, (float)y), 15.0f);
		HDC hdc = GetDC(hwnd);
		Render(hdc, hwnd, t2);
		DrawWall(hdc, newWall);
		ReleaseDC(hwnd, hdc);
	}
	else
		assert(t2->mhistory.size() == 0);
}

